做數據分析時,我們可能會很常使用jupyter notebook來操作
並且可能依照個人喜好來使用像是seaborn、matplotlib等工具把資料視覺化
但是如果是要呈現在網頁上的話,可能就要找JavaScript可視化庫會更有彈性
因此我選擇ECharts來作為這次的工具
https://echarts.apache.org/en/index.html
Apache ECharts的前身是百度的Echarts,在經過Apache Incubator孵化完成後變成Apache軟體基金會的頂級專案。ECharts是一個使用JavaScript實現的視覺化圖表庫,可以在PC與其他裝置上使用,且具有以下特點
豐富的圖表類型:
https://echarts.apache.org/examples/zh/index.html
除了一般常見的折線圖、柱狀圖、圓餅圖、散佈圖等等,還有包含k線圖、地理座標圖等等,並且也有許多酷炫的動畫呈現
方便:
Echarts內置的dataset屬性支持直接傳入array、key-value等多種格式的數據類型,省去很多時候數據還需要轉換的步驟
主題設計系統:
在示例中找到喜歡的圖點進去,就可以直接看每個圖應該要怎麼生成,並且也可以透過程式碼編輯馬上看到更改後的成果,非常強大與方便
我這邊就不特別介紹Django跟pandas的用法,今天要寫的語法都是很基礎的,所以也會直接掉過架設環境的部分
網路上有很多資源馬上找就有了。另外js的部分因為我對於js了解還很淺,覺得污染眼睛的話感到抱歉XDD
架構圖如上~
HTML:
Javascript:
Django:
以上就是大致的流程,其實每個圖都大同小異,只要會了其中一種剩下的也不會到太困難
動態的那些圖或是需要第三方計算回歸線那些,我不確定做起來後用pagespeed分析後會不會分數很慘XD
所以我自己在使用上應該還是簡單為主,另外我發現如果把幾種基本圖的code全部放上來,篇幅有點太長了
所以這次就先以折線圖來說~
這是官方示例的圖
接下來我們可以想一下,哪些部分是需要用到我們自己資料的
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>測試echart</title>
<script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>
<!--引入ECharts CDN-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.3/echarts.min.js"></script>
</head>
<body>
<!--設置DOM元素 並設置大小-->
<div id="ecahrtLine" style="width: 100%; height:50vh;"></div>
<!--js檔-->
<script src="/static/js/echart_theme/line.js"></script>
</body>
</html>
這邊就是引入CDN跟設置元素,我自己是還有再額外引入jqery,因為我在js有用到相關語法
Django-views.py:
from random import randrange
import pandas as pd
def create_line_data():
math_score = {
"math_score": [randrange(50, 90) for _ in range(6)]
}
english_score = {
"english_score": [randrange(60, 100) for _ in range(6)]
}
m_series = pd.Series(math_score)
e_series = pd.Series(english_score)
return m_series, e_series
def trans_df_to_list(series):
"""因為echart的data需要接收list 所以需要轉格式"""
if isinstance(series, pd.Series):
return series.values[0]
else:
return
def create_data(*args):
res = [i for i in args]
return res
def show_line(request):
m_series, e_series = create_line_data()
m_list = trans_df_to_list(m_series)
e_list = trans_df_to_list(e_series)
data = {
"code": 200,
"msg": "success",
"data": create_data(m_list, e_list),
}
return JsonResponse(data)
首先我這邊想要呈現的是兩個科目中,這一個班級的6位學生他們各自的分數
這邊可能要注意幾個點:
Javascript:
var lineDom = document.getElementById('ecahrtLine');
var myLine = echarts.init(lineDom);
$(
function () {
fetchData(myLine);
}
);
function fetchData() {
$.ajax({
url: "/article/show_line",
type: "GET",
dataType: "json",
success: function (result) {
var option = createOption(result.data);
myLine.setOption(option);
}
});
};
function createOption (backendData) {
// 做出x軸的列表
var indexList = backendData[0].map(function(_, index) {
return index + 1;
});
var option;
option = {
title: {
text: '數學與英文成績'
},
tooltip: {
trigger: 'axis'
},
legend: {},
toolbox: {
show: true,
feature: {
dataZoom: {
yAxisIndex: "none"
},
dataView: { readOnly: false },
magicType: { type: ['line', 'bar'] },
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: indexList,
name: "學生編號" // 設置名稱
},
yAxis: {
type: 'value',
min: "dataMin", // 設置y軸最小值
axisLabel: {
formatter: '{value} 分'
},
name: "成績" // 設置名稱
},
series: [
{
name: '數學成績',
type: 'line',
data: backendData[0], // 我們的資料
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
markLine: {
data: [{ type: 'average', name: 'Avg' }]
}
},
{
name: '英文成績',
type: 'line',
data: backendData[1], // 我們的資料
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
markLine: {
data: [
{ type: 'average', name: 'Avg' },
[
{
symbol: 'none',
x: '90%',
yAxis: 'max'
},
{
symbol: 'circle',
label: {
position: 'start',
formatter: 'Max'
},
type: 'max',
name: '最高點'
}
]
]
}
}
]
};
return option
}
因為我沒有特別需要轉換太多x軸的形式,所以我直接用索引來改編我的x軸,今天如果是x軸的資料格式比較特別,或是點超級多,建議在django那邊解決掉
其中在設置一些參數的API,我自己有改的部分有加上註解
如果還是看不懂,可以參考:
https://echarts.apache.org/zh/option.html#xAxis.name
這個官方文檔已經算是非常詳細的解釋API了,並且可以點“試一試”,再點code的部分便可以直接去操作測試
最後的成果如下:
甚至你可以點擊右上方的一些按鈕,會有很多不錯的特效
例如轉換成柱狀圖等等
但是這樣還不夠~
我們此時去更改視窗寬度,會發現圖表根本就沒有變化
而echarts有resize方法,可以讓圖表隨著視窗改變而改變
https://echarts.apache.org/zh/api.html#echartsInstance.resize
那一般我們沒有特別要求的話可以直接這樣調用
// 視窗調整時會更改echart圖表
window.onresize = function () {
myLine.resize()
};
這邊額外說一下,如果我們使用下圖這種圓餅圖
我們縮小的時候,會更希望由左右兩邊變成上下並行
echarts還有類似css中media的設置方法,可以參考官方文檔:
https://echarts.apache.org/zh/tutorial.html#%E7%A7%BB%E5%8A%A8%E7%AB%AF%E8%87%AA%E9%80%82%E5%BA%94
好~ 我們回到我們的折線圖,我們的確可以讓圖片的寬度自適應,但是文字不會因為圖片變小而讓佔比放大
這樣對於手機或是平板的使用者會非常痛苦
所以我們需要修改原本的方法,讓font-size也能夠自適應
參考:https://blog.csdn.net/jingjing217/article/details/114015832
原本的js邏輯順序如下
但是現在要修改成:
修改後的js
var lineDom = document.getElementById('ecahrtLine');
var myLine = echarts.init(lineDom);
var rowData;
$(function () {
fetchData(myLine);
}
);
// 因為文字不會更改 所以要自己寫方法
function fontMedia(fontSizePx){
var deviceWidth = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;
if (!deviceWidth) return;
var fontSize = 150 * (deviceWidth / 1920);
return fontSizePx*fontSize;
}
function fetchData() {
$.ajax({
url: "/article/show_line",
type: "GET",
dataType: "json",
success: function (result) {
rowData = result.data;
var option = createOption(result.data);
myLine.setOption(option);
}
});
};
function createOption (backendData) {
// 做出x軸的列表
var indexList = backendData[0].map(function(_, index) {
return index + 1;
});
var option;
option = {
title: {
text: '數學與英文成績',
textStyle: {
fontSize: fontMedia(0.4) // 讓標題可以隨之改變
}
},
tooltip: {
trigger: 'axis'
},
legend: {},
toolbox: {
show: true,
feature: {
dataZoom: {
yAxisIndex: "none"
},
dataView: { readOnly: false },
magicType: { type: ['line', 'bar'] },
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: indexList,
name: "學生編號" // 設置名稱
},
yAxis: {
type: 'value',
min: "dataMin", // 設置y軸最小值
axisLabel: {
formatter: '{value} 分'
},
name: "成績" // 設置名稱
},
series: [
{
name: '數學成績',
type: 'line',
data: backendData[0],
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
markLine: {
data: [{ type: 'average', name: 'Avg' }]
}
},
{
name: '英文成績',
type: 'line',
data: backendData[1],
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
markLine: {
data: [
{ type: 'average', name: 'Avg' },
[
{
symbol: 'none',
x: '90%',
yAxis: 'max'
},
{
symbol: 'circle',
label: {
position: 'start',
formatter: 'Max'
},
type: 'max',
name: '最高點'
}
]
]
}
}
]
};
return option
}
// 視窗調整時會更改echart圖表
window.onresize = function () {
var option = createOption(rowData);
myLine.setOption(option);
myLine.resize();
};
最後重整後,就可以發現我們的標題可以隨著視窗寬度變化而改變大小
有點懶得做gif所以直接丟圖XD
其他調整font-size就大同小異,就不示範了~
希望有幫助到想做圖的人~